import {
  ComputeOptions,
  computeValue,
  ExpressionOperator,
  Options
} from "../../../core";
import { Any, AnyObject } from "../../../types";
import { assert, isArray, isNil } from "../../../util";

/**
 * Applies an expression to each element in an array and combines them into a single value.
 *
 * @param obj
 * @param expr
 */
export const $reduce: ExpressionOperator = (
  obj: AnyObject,
  expr: AnyObject,
  options: Options
): Any => {
  const copts = ComputeOptions.init(options);
  const input = computeValue(obj, expr.input, null, copts) as Any[];
  const initialValue = computeValue(obj, expr.initialValue, null, copts);
  const inExpr = expr["in"];

  if (isNil(input)) return null;
  assert(isArray(input), "$reduce 'input' expression must resolve to an array");

  return input.reduce((acc, n) => {
    return computeValue(
      n,
      inExpr,
      null,
      copts.update(copts.root, {
        variables: { value: acc }
      })
    );
  }, initialValue);
};
